
#include "request.h"
#include <stdlib.h>
#include <stdarg.h>
#include <sys/queue.h>   // TAILQ_INIT
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#include <event2/buffer.h>

Request::Request(evhttp_request *req) :
	_request(req), response_buffer(evbuffer_new()),
	get_headers(new evkeyvalq), post_headers(new evkeyvalq),
	input_headers(evhttp_request_get_input_headers(_request)),
	output_headers(evhttp_request_get_output_headers(_request)),
	_path(NULL), get_query(NULL), post_query(NULL)
{
	TAILQ_INIT(get_headers);
	TAILQ_INIT(post_headers);

	parseGetHeaders();
	parsePostHeaders();
}

Request::~Request() {
	if (get_query) free(get_query);
	if (post_query) free(post_query);
	delete post_headers;
	delete get_headers;
	evbuffer_free(response_buffer);
}

void Request::addHeader(const char *name, const char *val) {
	evhttp_add_header(output_headers, name, val);
}

bool Request::removeHeader(const char *name) {
	return !evhttp_remove_header(output_headers, name);
}

void Request::addResponse(const char *format, ...) {
	va_list args;
	va_start(args, format);
	evbuffer_add_vprintf(response_buffer, format, args);
	va_end(args);
}

void Request::addBinaryResponse(const void *response, size_t size) {
	evbuffer_add(response_buffer, response, size);
}

void Request::sendResponse() {
	evhttp_send_reply(_request, HTTP_OK, "OK", response_buffer);
}

void Request::sendError(int code) {
	evhttp_send_error(_request, code, NULL);
}

const char *Request::path() const {
	return (_path ? _path : "/");
}

const char *Request::getQuery() const {
	return get_query;
}

const char *Request::postQuery() const {
	return post_query;
}

const char *Request::getVar(const char *var) const {
	return evhttp_find_header(get_headers, var);
}

const char *Request::postVar(const char *var) const {
	return evhttp_find_header(post_headers, var);
}

void Request::parseGetHeaders() {
	evhttp_clear_headers(get_headers);

	const evhttp_uri *uri = evhttp_request_get_evhttp_uri(_request);
	if (uri) {
		_path = evhttp_uri_get_path(uri);
		const char *tmp = evhttp_uri_get_query(uri);
		if (tmp) {
			int sz = strlen(tmp);
			get_query = (char *)calloc(sz + 1, sizeof(char));
			//get_query = evhttp_uri_get_query(uri);
			if (get_query) {
				strncpy(get_query, tmp, sz);
				parseQuery(get_query, get_headers); // if it fails, it doesn't matter
			} // else forget it -- assume we have empty GET request
		} else {
			get_query = (char *)malloc(sizeof(char));
			get_query[0] = '\0';  // empty string
		}
	} // else forget it -- assume we have empty GET request
}

void Request::parsePostHeaders() {
	evhttp_clear_headers(post_headers);

	const char *cltxt = evhttp_find_header(input_headers, "Content-Length");
	if (cltxt) {
		int cl = atoi(cltxt);
		if (cl > 0) { // perhaps we have something POSTed...
			if (post_query) free(post_query);
			post_query = (char *)calloc(cl + 1, sizeof(char)); // +1 for \0
			if (post_query) {
				strncpy(post_query, (char *)evbuffer_pullup(evhttp_request_get_input_buffer(_request), cl), cl);
				parseQuery(post_query, post_headers); // if it fails, it doesn't matter
			} // else no memory anyway
		} // else nothing POSTed, stay idle
	} // else no header 'Content-Length', ignore it (perhaps no POST data)
}

bool Request::parseQuery(const char *src, evkeyvalq *dest) {
	return evhttp_parse_query_str(src, dest);
}
